﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Gezel.Model;
using Weazel.Gezel.Model.Errors;

#endregion

namespace Weazel.Gezel.Parser
{
  public class UnexpectedTokenParseError : ParseError
  {
    private Position position;
    private Token tokenFound;
    private Set<Token> tokens = new Set<Token>();

    private void formatError()
    {
      System.Diagnostics.Debug.Assert(tokens.Count > 0);

      if (tokens.Count == 1)
      {
        errorMessage = new ErrorMessage();
        errorMessage.AddString(
          string.Format(
            "Expected '{0}' found '{1}' at ", 
            Tokenizer.TokenToString(tokens[0]), 
            Tokenizer.TokenToString(tokenFound)
          )
        );
        errorMessage.AddPosition(position);
      }
      else
      {
        errorMessage = new ErrorMessage();
        errorMessage.AddString(
          string.Format(
            "Unexpected token '{0}' found at ",
            Tokenizer.TokenToString(tokenFound)
          )
        );

        errorMessage.AddPosition(position);
      }
    }

    public UnexpectedTokenParseError(Position position, Token tokenFound, Token token) : 
      base(ParseErrorType.UnexpectedTokenExpectedSingle)
    {
      this.position = position;
      this.tokenFound = tokenFound;
      this.tokens.Add(token);

      formatError();
    }

    public UnexpectedTokenParseError(Position position, Token tokenFound, params Token[] tokens) : 
      base(tokens.Length > 1 ? ParseErrorType.UnexpectedTokenExpectedMultiple : 
      ParseErrorType.UnexpectedTokenExpectedSingle)
    {
      this.position = position;
      this.tokenFound = tokenFound;
      this.tokens.AddRange(tokens);

      formatError();
    }

    public UnexpectedTokenParseError(Position position, Token tokenFound, Set<Token> tokens) :
      base(tokens.Count > 1 ? ParseErrorType.UnexpectedTokenExpectedMultiple : 
      ParseErrorType.UnexpectedTokenExpectedSingle)
    {
      this.position = position;
      this.tokenFound = tokenFound;
      this.tokens = tokens;

      formatError();
    }
  }

  public class DuplicateIpTypeParseError : ParseError
  {
    private IPBlock ipBlock;

    public DuplicateIpTypeParseError(IPBlock ipblock) : 
      base(ParseErrorType.DuplicateIpType)
    {
      this.ipBlock = ipblock;

      errorMessage = new ErrorMessage("Two or more 'iptype' elements found in ip block '");
      errorMessage.AddEntity(ipBlock);
      errorMessage.AddString("' at ");
      errorMessage.AddPosition(ipblock.StartPosition);
      errorMessage.AddString(". Only one allowed.");   
    }
  }

  public class DuplicateTransitionParseError : ParseError
  {
    private Controller controller;
    private Transition transition1;
    private Transition transition2;
    private string source;

    public DuplicateTransitionParseError(Transition transition1, Transition transition2, string sourceState, Controller controller) : 
      base(ParseErrorType.DuplicateTranstion)
    {
      this.transition1 = transition1;
      this.transition2 = transition2;
      this.source = sourceState;
      this.controller = controller;

      errorMessage = new ErrorMessage("Duplicate use of source state '" + source + "' in transition found at ");
      errorMessage.AddPosition(transition1.StartPosition);
      errorMessage.AddString(". Previously used at ");
      errorMessage.AddPosition(transition2.StartPosition);
    }
  }

  public class DuplicateControllerNameParseError : ParseError
  {
    private Controller controller1;
    private Controller controller2;

    public DuplicateControllerNameParseError(Controller controller1, Controller controller2) : 
      base(ParseErrorType.DuplicateControllerDefinition)
    {
      this.controller1 = controller1;
      this.controller2 = controller2;

      errorMessage = new ErrorMessage("Duplicate definition of controller '");
      errorMessage.AddEntity(controller1);
      errorMessage.AddString("' at ");
      errorMessage.AddPosition(controller1.StartPosition);
      errorMessage.AddString(". Previously defined at ");
      errorMessage.AddPosition(controller2.StartPosition);
    }
  }

  public class DuplicateDatapathNameParseError : ParseError
  {
    private Datapath datapath1;
    private Datapath datapath2;

    public DuplicateDatapathNameParseError(Datapath dp1, Datapath dp2) : 
      base(ParseErrorType.DuplicateDatapathDefintion)
    {
      this.datapath1 = dp1;
      this.datapath2 = dp2;

      errorMessage = new ErrorMessage("Duplicate definition of datapath '");
      errorMessage.AddEntity(dp1);
      errorMessage.AddString("' at ");
      errorMessage.AddPosition(dp1.StartPosition);
      errorMessage.AddString(". Previously defined at ");
      errorMessage.AddPosition(dp2.StartPosition);
    }
  }

  public class DuplicateSignalFlowGraphNameParseError : ParseError
  {
    private SignalFlowGraph sfg1;
    private SignalFlowGraph sfg2;
    private Datapath datapath;

    #region Properties
    public SignalFlowGraph First
    {
      get
      {
        return sfg1;
      }
    }

    public SignalFlowGraph Second
    {
      get
      {
        return sfg2;
      }
    }

    public Datapath Datapath
    {
      get
      {
        return datapath;
      }
    }
    #endregion

    public DuplicateSignalFlowGraphNameParseError(SignalFlowGraph sfg1, SignalFlowGraph sfg2, Datapath datapath) :
      base(ParseErrorType.DuplicateSfgDefinition)
    {
      this.sfg1 = sfg1;
      this.sfg2 = sfg2;
      this.datapath = datapath;

      errorMessage = new ErrorMessage("Duplicate definition of signal flow graph '");
      errorMessage.AddEntity(sfg1);
      errorMessage.AddString("' at ");
      errorMessage.AddPosition(sfg1.StartPosition);
      errorMessage.AddString(". Previously defined at ");
      errorMessage.AddPosition(sfg2.StartPosition);
    }
  }

  public class DuplicateSymbolDefinitionParseError : ParseError
  {
    private CommonEntity first;
    private CommonEntity second;
    private Datapath datapath;

    #region Properties
    public CommonEntity First
    {
      get
      {
        return first;
      }
    }

    public CommonEntity Second
    {
      get
      {
        return second;
      }
    }

    public Datapath Datapath
    {
      get
      {
        return datapath;
      }
    }
    #endregion

    /// <summary>
    /// Checks if the symbol passed in as an argument is already used in the
    /// scope of the datapath.
    /// </summary>
    /// <param name="dp">Datapath to check</param>
    /// <param name="symbol">symbol to check</param>
    /// <returns>true iff no error</returns>
    public static bool Check(Datapath dp, string symbol)
    {
      bool fail =
        dp.Signals.ContainsKey(symbol) ||
        dp.Registers.ContainsKey(symbol) ||
        dp.InPorts.ContainsKey(symbol) ||
        dp.OutPorts.ContainsKey(symbol);;

      return !fail;
    }

    private void format()
    {
      errorMessage = new ErrorMessage("A signal, register or port named '");
      errorMessage.AddEntity(first);
      errorMessage.AddString("' is already defined in datapath '");
      errorMessage.AddEntity(datapath);
      errorMessage.AddString("'");
    }

    public DuplicateSymbolDefinitionParseError(ParseErrorType type, CommonEntity s1, CommonEntity s2, Datapath dp) : 
      base(type)
    {
      this.first = s1;
      this.second = s2;
      this.datapath = dp;

      format();
    }

    public DuplicateSymbolDefinitionParseError(Signal s1, Signal s2, Datapath dp) : 
      this(ParseErrorType.DuplicateSymbolDefintion, s1, s2, dp)
    {
      System.Diagnostics.Debug.Assert(s1.LocalName == s2.LocalName);
    }

    public DuplicateSymbolDefinitionParseError(Signal s1, Register s2, Datapath dp) : 
      this(ParseErrorType.DuplicateSymbolDefintion, s1, s2, dp)
    {
      System.Diagnostics.Debug.Assert(s1.LocalName == s2.LocalName);
    }

    public DuplicateSymbolDefinitionParseError(Register s1, Signal s2, Datapath dp) : 
      this(ParseErrorType.DuplicateSymbolDefintion, s1, s2, dp)
    {
      System.Diagnostics.Debug.Assert(s1.LocalName == s2.LocalName);
    }

    public DuplicateSymbolDefinitionParseError(Signal s1, Port s2, Datapath dp) : 
      this(ParseErrorType.DuplicateSymbolDefintion, s1, s2, dp)
    {
      System.Diagnostics.Debug.Assert(s1.LocalName == s2.LocalName);
    }

    public DuplicateSymbolDefinitionParseError(Port s1, Signal s2, Datapath dp) : 
      this(ParseErrorType.DuplicateSymbolDefintion, s1, s2, dp)
    {
      System.Diagnostics.Debug.Assert(s1.LocalName == s2.LocalName);
    }

    public DuplicateSymbolDefinitionParseError(Register s1, Port s2, Datapath dp) : 
      this(ParseErrorType.DuplicateSymbolDefintion, s1, s2, dp)
    {
    }

    public DuplicateSymbolDefinitionParseError(Port s1, Register s2, Datapath dp) : 
      this(ParseErrorType.DuplicateSymbolDefintion, s1, s2, dp)
    {
      System.Diagnostics.Debug.Assert(s1.LocalName == s2.LocalName);
    }

    public DuplicateSymbolDefinitionParseError(Register s1, Register s2, Datapath dp) : 
      this(ParseErrorType.DuplicateSymbolDefintion, s1, s2, dp)
    {
      System.Diagnostics.Debug.Assert(s1.LocalName == s2.LocalName);
    }

    public DuplicateSymbolDefinitionParseError(Port s1, Port s2, Datapath dp) : 
      this(ParseErrorType.DuplicateSymbolDefintion, s1, s2, dp)
    {
      System.Diagnostics.Debug.Assert(s1.LocalName == s2.LocalName);
    }
  }
}
